"""
认证中间件 - 提供JWT认证、权限检查等安全功能
支持多种认证策略、角色权限管理、会话管理等
"""

import jwt
import hashlib
import time
from typing import (
    Optional, List, Dict, Any, Callable, Union, Set,
    Protocol, runtime_checkable
)
from dataclasses import dataclass, field
from enum import Enum
from abc import ABC, abstractmethod
from datetime import datetime, timedelta

from fastapi import Request, Response, HTTPException, status
from fastapi.responses import JSONResponse
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

from .manager import BaseMiddleware, MiddlewarePhase, MiddlewarePriority
from ..logging.manager import get_logger
from ..events.bus import Event, EventBus

class AuthenticationMethod(Enum):
    """认证方法"""
    JWT = "jwt"
    BEARER = "bearer"
    API_KEY = "api_key"
    SESSION = "session"
    BASIC = "basic"

class Permission(Enum):
    """基础权限"""
    READ = "read"
    WRITE = "write"
    DELETE = "delete"
    ADMIN = "admin"

@dataclass
class User:
    """用户信息"""
    user_id: str
    username: str
    email: Optional[str] = None
    roles: List[str] = field(default_factory=list)
    permissions: List[str] = field(default_factory=list)
    metadata: Dict[str, Any] = field(default_factory=dict)
    is_active: bool = True
    created_at: Optional[datetime] = None
    last_login: Optional[datetime] = None

@dataclass
class AuthContext:
    """认证上下文"""
    user: Optional[User] = None
    is_authenticated: bool = False
    method: Optional[AuthenticationMethod] = None
    token: Optional[str] = None
    session_id: Optional[str] = None
    ip_address: Optional[str] = None
    user_agent: Optional[str] = None
    expires_at: Optional[datetime] = None

@runtime_checkable
class IAuthProvider(Protocol):
    """认证提供者接口"""
    
    async def authenticate(self, request: Request) -> Optional[AuthContext]:
        """认证用户"""
        ...
    
    async def authorize(self, context: AuthContext, required_permissions: List[str]) -> bool:
        """授权检查"""
        ...

@runtime_checkable
class IUserProvider(Protocol):
    """用户提供者接口"""
    
    async def get_user(self, user_id: str) -> Optional[User]:
        """获取用户信息"""
        ...
    
    async def validate_credentials(self, username: str, password: str) -> Optional[User]:
        """验证用户凭据"""
        ...

class JWTAuthProvider(IAuthProvider):
    """JWT认证提供者"""
    
    def __init__(
        self,
        secret_key: str,
        algorithm: str = "HS256",
        token_expire_minutes: int = 30,
        user_provider: Optional[IUserProvider] = None
    ):
        self.secret_key = secret_key
        self.algorithm = algorithm
        self.token_expire_minutes = token_expire_minutes
        self.user_provider = user_provider
        self.logger = get_logger("jwt_auth")
    
    async def authenticate(self, request: Request) -> Optional[AuthContext]:
        """JWT认证"""
        # 从Authorization头获取token
        authorization = request.headers.get("authorization")
        if not authorization or not authorization.startswith("Bearer "):
            return None
        
        token = authorization[7:]  # 移除 "Bearer " 前缀
        
        try:
            # 解码JWT
            payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
            
            # 检查过期时间
            exp = payload.get("exp")
            if exp and datetime.fromtimestamp(exp) < datetime.utcnow():
                return None
            
            user_id = payload.get("sub")
            if not user_id:
                return None
            
            # 获取用户信息
            user = None
            if self.user_provider:
                user = await self.user_provider.get_user(user_id)
            else:
                # 从token中构造用户信息
                user = User(
                    user_id=user_id,
                    username=payload.get("username", user_id),
                    email=payload.get("email"),
                    roles=payload.get("roles", []),
                    permissions=payload.get("permissions", [])
                )
            
            return AuthContext(
                user=user,
                is_authenticated=True,
                method=AuthenticationMethod.JWT,
                token=token,
                ip_address=request.client.host if request.client else None,
                user_agent=request.headers.get("user-agent"),
                expires_at=datetime.fromtimestamp(exp) if exp else None
            )
            
        except jwt.ExpiredSignatureError:
            self.logger.warning("JWT token expired")
            return None
        except jwt.InvalidTokenError as e:
            self.logger.warning(f"Invalid JWT token: {e}")
            return None
        except Exception as e:
            self.logger.error(f"JWT authentication error: {e}")
            return None
    
    async def authorize(self, context: AuthContext, required_permissions: List[str]) -> bool:
        """JWT授权检查"""
        if not context.is_authenticated or not context.user:
            return False
        
        user_permissions = set(context.user.permissions)
        required_permissions_set = set(required_permissions)
        
        # 检查是否有所需权限
        return required_permissions_set.issubset(user_permissions)
    
    def create_token(self, user: User, expires_delta: Optional[timedelta] = None) -> str:
        """创建JWT token"""
        if expires_delta is None:
            expires_delta = timedelta(minutes=self.token_expire_minutes)
        
        expire = datetime.utcnow() + expires_delta
        
        payload = {
            "sub": user.user_id,
            "username": user.username,
            "email": user.email,
            "roles": user.roles,
            "permissions": user.permissions,
            "exp": expire,
            "iat": datetime.utcnow(),
            "iss": "redfire_framework"
        }
        
        return jwt.encode(payload, self.secret_key, algorithm=self.algorithm)

class APIKeyAuthProvider(IAuthProvider):
    """API密钥认证提供者"""
    
    def __init__(self, api_keys: Dict[str, User]):
        self.api_keys = api_keys
        self.logger = get_logger("apikey_auth")
    
    async def authenticate(self, request: Request) -> Optional[AuthContext]:
        """API Key认证"""
        # 从头部或查询参数获取API Key
        api_key = request.headers.get("x-api-key") or request.query_params.get("api_key")
        
        if not api_key:
            return None
        
        # 验证API Key
        user = self.api_keys.get(api_key)
        if not user or not user.is_active:
            return None
        
        return AuthContext(
            user=user,
            is_authenticated=True,
            method=AuthenticationMethod.API_KEY,
            token=api_key,
            ip_address=request.client.host if request.client else None,
            user_agent=request.headers.get("user-agent")
        )
    
    async def authorize(self, context: AuthContext, required_permissions: List[str]) -> bool:
        """API Key授权检查"""
        if not context.is_authenticated or not context.user:
            return False
        
        user_permissions = set(context.user.permissions)
        required_permissions_set = set(required_permissions)
        
        return required_permissions_set.issubset(user_permissions)

class AuthenticationMiddleware(BaseMiddleware):
    """认证中间件"""
    
    def __init__(
        self,
        auth_provider: IAuthProvider,
        excluded_paths: List[str] = None,
        optional_auth_paths: List[str] = None,
        event_bus: EventBus = None
    ):
        super().__init__("AuthenticationMiddleware")
        self.auth_provider = auth_provider
        self.excluded_paths = excluded_paths or []
        self.optional_auth_paths = optional_auth_paths or []
        self.event_bus = event_bus
    
    def should_process(self, request: Request) -> bool:
        """判断是否需要认证"""
        path = request.url.path
        
        # 检查排除路径
        for excluded_path in self.excluded_paths:
            if path.startswith(excluded_path):
                return False
        
        return True
    
    async def process_request(self, request: Request) -> Optional[Response]:
        """处理认证"""
        if not self.should_process(request):
            return None
        
        path = request.url.path
        is_optional = any(path.startswith(opt_path) for opt_path in self.optional_auth_paths)
        
        # 执行认证
        auth_context = await self.auth_provider.authenticate(request)
        
        if not auth_context or not auth_context.is_authenticated:
            if is_optional:
                # 可选认证路径，继续处理
                request.state.auth_context = AuthContext()
                return None
            else:
                # 必需认证，返回401错误
                self.logger.warning(
                    "Authentication failed",
                    path=path,
                    ip=request.client.host if request.client else None
                )
                
                # 发布认证失败事件（如果有事件总线）
                if self.event_bus:
                    self.event_bus.publish(Event(
                        event_type="auth.failed",
                        data={
                            "path": path,
                            "ip": request.client.host if request.client else None,
                            "user_agent": request.headers.get("user-agent")
                    }
                ))
                
                return JSONResponse(
                    status_code=status.HTTP_401_UNAUTHORIZED,
                    content={"error": "Authentication required"}
                )
        
        # 保存认证上下文到请求状态
        request.state.auth_context = auth_context
        
        # 发布认证成功事件（如果有事件总线）
        if auth_context.is_authenticated and self.event_bus:
            self.event_bus.publish(Event(
                event_type="auth.success",
                data={
                    "user_id": auth_context.user.user_id if auth_context.user else None,
                    "method": auth_context.method.value if auth_context.method else None,
                    "path": path,
                    "ip": auth_context.ip_address
                }
            ))
        
        return None

class AuthorizationMiddleware(BaseMiddleware):
    """授权中间件"""
    
    def __init__(
        self,
        auth_provider: IAuthProvider,
        path_permissions: Dict[str, List[str]] = None,
        default_permissions: List[str] = None,
        event_bus: EventBus = None
    ):
        super().__init__("AuthorizationMiddleware")
        self.auth_provider = auth_provider
        self.path_permissions = path_permissions or {}
        self.default_permissions = default_permissions or []
        self.event_bus = event_bus
    
    async def process_request(self, request: Request) -> Optional[Response]:
        """处理授权"""
        # 获取认证上下文
        auth_context = getattr(request.state, 'auth_context', None)
        if not auth_context:
            return None
        
        # 如果未认证，跳过授权检查
        if not auth_context.is_authenticated:
            return None
        
        # 获取所需权限
        path = request.url.path
        required_permissions = self._get_required_permissions(path, request.method)
        
        if not required_permissions:
            return None
        
        # 检查权限
        has_permission = await self.auth_provider.authorize(auth_context, required_permissions)
        
        if not has_permission:
            self.logger.warning(
                "Authorization failed",
                user_id=auth_context.user.user_id if auth_context.user else None,
                path=path,
                method=request.method,
                required_permissions=required_permissions,
                user_permissions=auth_context.user.permissions if auth_context.user else []
            )
            
            # 发布授权失败事件（如果有事件总线）
            if self.event_bus:
                self.event_bus.publish(Event(
                    event_type="auth.unauthorized",
                    data={
                        "user_id": auth_context.user.user_id if auth_context.user else None,
                        "path": path,
                        "method": request.method,
                        "required_permissions": required_permissions
                }
            ))
            
            return JSONResponse(
                status_code=status.HTTP_403_FORBIDDEN,
                content={"error": "Insufficient permissions"}
            )
        
        return None
    
    def _get_required_permissions(self, path: str, method: str) -> List[str]:
        """获取路径所需权限"""
        # 检查精确匹配
        if path in self.path_permissions:
            return self.path_permissions[path]
        
        # 检查前缀匹配
        for pattern, permissions in self.path_permissions.items():
            if path.startswith(pattern):
                return permissions
        
        # 返回默认权限
        return self.default_permissions

class RateLimitMiddleware(BaseMiddleware):
    """限流中间件"""
    
    def __init__(
        self,
        requests_per_minute: int = 60,
        burst_size: int = 10,
        key_func: Optional[Callable[[Request], str]] = None
    ):
        super().__init__("RateLimitMiddleware")
        self.requests_per_minute = requests_per_minute
        self.burst_size = burst_size
        self.key_func = key_func or self._default_key_func
        self.request_counts: Dict[str, List[float]] = {}
    
    def _default_key_func(self, request: Request) -> str:
        """默认限流键函数 - 使用IP地址"""
        return request.client.host if request.client else "unknown"
    
    async def process_request(self, request: Request) -> Optional[Response]:
        """处理限流"""
        key = self.key_func(request)
        current_time = time.time()
        
        # 获取或初始化请求计数
        if key not in self.request_counts:
            self.request_counts[key] = []
        
        requests = self.request_counts[key]
        
        # 清理过期的请求记录（超过1分钟）
        cutoff_time = current_time - 60
        requests[:] = [req_time for req_time in requests if req_time > cutoff_time]
        
        # 检查请求数量限制
        if len(requests) >= self.requests_per_minute:
            self.logger.warning(
                "Rate limit exceeded",
                key=key,
                requests_count=len(requests),
                limit=self.requests_per_minute
            )
            
            return JSONResponse(
                status_code=status.HTTP_429_TOO_MANY_REQUESTS,
                content={
                    "error": "Rate limit exceeded",
                    "retry_after": 60
                },
                headers={"Retry-After": "60"}
            )
        
        # 记录当前请求
        requests.append(current_time)
        
        return None

# 便捷函数
def create_jwt_auth_middleware(
    secret_key: str,
    excluded_paths: List[str] = None,
    optional_auth_paths: List[str] = None,
    **jwt_config
) -> AuthenticationMiddleware:
    """创建JWT认证中间件"""
    provider = JWTAuthProvider(secret_key, **jwt_config)
    return AuthenticationMiddleware(
        auth_provider=provider,
        excluded_paths=excluded_paths,
        optional_auth_paths=optional_auth_paths
    )

def create_api_key_auth_middleware(
    api_keys: Dict[str, User],
    excluded_paths: List[str] = None
) -> AuthenticationMiddleware:
    """创建API Key认证中间件"""
    provider = APIKeyAuthProvider(api_keys)
    return AuthenticationMiddleware(
        auth_provider=provider,
        excluded_paths=excluded_paths
    )

# 装饰器
def require_auth(permissions: List[str] = None):
    """需要认证的装饰器"""
    def decorator(func):
        func._auth_required = True
        func._required_permissions = permissions or []
        return func
    return decorator

def require_permissions(*permissions: str):
    """需要权限的装饰器"""
    def decorator(func):
        func._required_permissions = list(permissions)
        return func
    return decorator

# 示例使用
if __name__ == "__main__":
    from ..middleware.manager import get_middleware_manager, MiddlewarePriority
    
    # 创建用户
    admin_user = User(
        user_id="admin",
        username="admin",
        email="admin@example.com",
        roles=["admin"],
        permissions=["read", "write", "delete", "admin"]
    )
    
    api_user = User(
        user_id="api_user",
        username="api_user", 
        permissions=["read", "write"]
    )
    
    # JWT认证示例
    jwt_auth = create_jwt_auth_middleware(
        secret_key="your-secret-key",
        excluded_paths=["/health", "/docs", "/openapi.json"],
        optional_auth_paths=["/public"]
    )
    
    # API Key认证示例
    api_keys = {
        "sk-1234567890": api_user,
        "sk-admin-key": admin_user
    }
    
    apikey_auth = create_api_key_auth_middleware(
        api_keys=api_keys,
        excluded_paths=["/health", "/docs"]
    )
    
    # 注册中间件
    manager = get_middleware_manager()
    
    manager.register(
        jwt_auth,
        name="jwt_auth",
        priority=MiddlewarePriority.HIGH,
        phases=[MiddlewarePhase.BEFORE_REQUEST]
    )
    
    # 限流中间件
    rate_limit = RateLimitMiddleware(requests_per_minute=100)
    manager.register(
        rate_limit,
        name="rate_limit",
        priority=MiddlewarePriority.HIGHEST
    )
